package com.sharer.last.utils;


import cn.hutool.core.date.DateUtil;
import com.sharer.last.base.utils.StringUtil;
import org.apache.commons.lang3.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
 * 日期工具类,
 * 继承 hutool DateUtil
 */
public class DateTimeUtil extends DateUtil {


    public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
    public static final String PATTERN_DATE = "yyyy-MM-dd";
    public static final String PATTERN_TIME = "HH:mm:ss";

    public static final String YMD_HMS = "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-9])))\\s([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])";
    public static final String YMD = "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-9])))";

    public static final String YMD_T_HMS = "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-9])))T([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])";
    public static final String YMD_T_HMS_SSS = "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-9])))T([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]).([0-9][0-9][0-9])";


    public static final String PATTERN_DATETIME_STR = "yyyyMMddHHmmss";

    public static final String PATTERN_DATE_STR = "yyyyMMdd";
    public static final String PATTERN_DATE_EXCEL = "yyyy/MM/dd";
    public static final String PATTERN_DATE_EXCEL_SHORT = "yyyy/m/d";
    public static final String PATTERN_DATE_CN = "yyyy年MM月dd日";

    public static final int WORK_HOUR_START = 9;
    public static final int WORK_HOUR_END = 18;

    /**
     * 获取当前时间戳
     *
     * @return nowTimeStamp
     */
    public static long getTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 获取当前时间格式（yyyyMMddHHmmss）
     *
     * @return
     */
    public static String getTimestampStr() {
        return format(new Date(), PATTERN_DATETIME_STR);
    }

    /***是否符合对应日期格式
     *
     * @param timestampStr
     * @return
     */
    public static boolean isTimestampStr(String timestampStr) {
        return isDate(timestampStr, PATTERN_DATETIME_STR);
    }

    /**
     * 获取Unix时间戳
     *
     * @return
     */
    public static long getUnixTimestamp() {
        long nowTimeStamp = System.currentTimeMillis() / 1000;
        return nowTimeStamp;
    }

    public static long getUnixTimestamp(LocalDateTime curDateTime) {
        return curDateTime.toEpochSecond(ZoneOffset.of("+8"));
    }

    /***多少天之前的时间戳
     *
     * @param curDateTime
     * @param days
     * @return
     */
    public static long getTimestampBefore(LocalDateTime curDateTime, int days) {
        return getUnixTimestamp(curDateTime.minusDays(days));
    }

    /**
     * 判断字符串是否是符合指定格式的时间
     *
     * @param date   时间字符串
     * @param format 时间格式
     * @return 是否符合
     */
    public static boolean isDate(String date, String format) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            sdf.parse(date);
            return true;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * localDateToDate
     *
     * @param localDate 时间
     * @return Date
     */
    public static Date localDateToDate(LocalDate localDate) {
        if (localDate == null) {
            return null;
        }
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * dateToLocalDate
     *
     * @param date date
     * @return LocalDate
     */
    public static LocalDate dateToLocalDate(Date date) {
        if (date == null) {
            return null;
        }
        return dateToLocalDateTime(date).toLocalDate();
    }

    /**
     * localTimeToDate
     *
     * @param localTime 时间
     * @return Date
     */
    public static Date localTimeToDate(LocalTime localTime, LocalDate localDate) {
        if (localTime == null) {
            localTime = LocalTime.now();
        }
        if (localDate == null) {
            localDate = LocalDate.now();
        }
        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * dateToLocalTime
     *
     * @param date date
     * @return LocalTime
     */
    public static LocalTime dateToLocalTime(Date date) {
        if (date == null) {
            return null;
        }
        return dateToLocalDateTime(date).toLocalTime();
    }

    /**
     * localDateTime2Date
     *
     * @param dateTime 时间
     * @return Date
     */
    public static Date localDateTime2Date(LocalDateTime dateTime) {
        return localDateTimeToDate(dateTime);
    }

    /**
     * date2LocalDateTime
     *
     * @param date date
     * @return LocalDateTime
     */
    public static LocalDateTime date2LocalDateTime(Date date) {
        return dateToLocalDateTime(date);
    }


    public static Date localDateTimeToDate(LocalDateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * date2LocalDateTime
     *
     * @param date date
     * @return LocalDateTime
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }


    /***时间戳转日期
     *
     * @param lt
     * @return
     */
    public static String timestampToDate(long lt) {
        String res;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(PATTERN_DATETIME);
        Date date = new Date(lt);
        res = simpleDateFormat.format(date);
        return res;
    }

    /**
     * 解析时间
     */
    public static LocalDateTime parseOfPattern(String dateStr) {
        if (StringUtil.isNotEmpty(dateStr)) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern(PATTERN_DATETIME);
            return LocalDateTime.parse(dateStr, df);
        } else {
            return null;
        }
    }

    /**
     * 日期解析
     */
    public static LocalDateTime parseOfPattern(String dateStr, String pattern) {
        if (StringUtil.isNotEmpty(dateStr)) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
            return LocalDateTime.parse(dateStr, df);
        } else {
            return null;
        }
    }

    /***格式化日期
     * @param dateTime
     * @return
     */
    public static String formatDateTime(LocalDateTime dateTime) {
        if (dateTime == null) {
            return "";
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_DATETIME);
        return dateTime.format(formatter);
    }

    /***格式化日期
     * @param dateTime
     * @return
     */
    public static String formatDate(LocalDate dateTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_DATE);
        return dateTime.format(formatter);
    }

    /***按格式格式化日期
     *
     * @param dateTime
     * @param pattern
     * @return
     */
    public static String formatDateTime(LocalDateTime dateTime, String pattern) {
        if (dateTime == null) {
            return "";
        }
        ZonedDateTime zdTime = dateTime.atZone(ZoneId.systemDefault());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return zdTime.format(formatter);
    }

    /***按格式格式化日期 English
     *
     * @param dateTime
     * @param pattern
     * @return
     */
    public static String formatDateTimeEnglish(LocalDateTime dateTime, String pattern) {
        if (dateTime == null) {
            return "";
        }
        ZonedDateTime zdTime = dateTime.atZone(ZoneId.systemDefault());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH);
        return zdTime.format(formatter);
    }

    /**
     * 时间加秒
     */
    public static LocalDateTime addDateTime(LocalDateTime dateTime, int second) {
        return dateTime.plusSeconds(second);
    }


    /**
     * 1、字符串格式:yyyy-MM-dd 解析日期为 yyyy-MM-dd 00:00:00，
     * 2、字符串格式:yyyy-MM-dd HH:mm:ss 解析日期为 yyyy-MM-dd HH:mm:ss，
     */
    public static LocalDateTime parseToDateTime(String dateStr) {

        if (StringUtils.isBlank(dateStr)) {
            return null;
        }
        LocalDateTime localDateTime;

        Integer formatResult = judgeDateFormat(dateStr);
        if (formatResult == 0) {
            localDateTime = LocalDateTime.parse(StringUtils.join(dateStr, " 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        } else if (formatResult == 1) {
            localDateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        } else if (formatResult == 2) {
            localDateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
        } else if (formatResult == 3) {
            localDateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"));
        } else {
            if (isDate(dateStr, PATTERN_DATE)) {
                return parseOfDate(dateStr);
            } else if (isDate(dateStr, PATTERN_DATETIME)) {
                return parseOfPattern(dateStr);
            } else if (isDate(dateStr, PATTERN_DATE_EXCEL)) {
                return parseOfPattern(dateStr + " 00:00:00", PATTERN_DATE_EXCEL + " " + PATTERN_TIME);
            }
            return null;
        }

        return localDateTime;
    }

    /**
     * 日期字符串转成 LocalDate
     */
    public static LocalDate parseToDate(String dateStr) {
        if (isDate(dateStr, PATTERN_DATE)) {
            return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(PATTERN_DATE));
        } else if (isDate(dateStr, PATTERN_DATE_STR)) {
            return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(PATTERN_DATE_STR));
        } else if (isDate(dateStr, PATTERN_DATE_EXCEL)) {
            return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(PATTERN_DATE_EXCEL));
        } else if (isDate(dateStr, PATTERN_DATE_EXCEL_SHORT)) {
            return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(PATTERN_DATE_EXCEL_SHORT));
        } else if (isDate(dateStr, PATTERN_DATE_CN)) {
            return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(PATTERN_DATE_CN));
        }
        return null;
    }


    /**
     * 判断是日期格式，是yyyy-MM-dd 还是yyyy-MM-dd HH:mm:ss
     *
     * @return 0:yyyy-MM-dd   1:yyyy-MM-dd HH:mm:ss   2:都不是
     */

    private static int judgeDateFormat(String dateStr) {
        Pattern patternYMD = Pattern.compile(YMD);
        Matcher matcherYMD = patternYMD.matcher(dateStr);
        Pattern patternYMDHHMMSS = Pattern.compile(YMD_HMS);
        Matcher matcherYMDHHMMSS = patternYMDHHMMSS.matcher(dateStr);

        Pattern patternYMDTHMS = Pattern.compile(YMD_T_HMS);
        Matcher matcherYMDTHMS = patternYMDTHMS.matcher(dateStr);

        Pattern patternYMDTHMSSSS = Pattern.compile(YMD_T_HMS_SSS);
        Matcher matcherYMDTHMSSSS = patternYMDTHMSSSS.matcher(dateStr);

        if (matcherYMD.matches()) {
            return 0;
        } else if (matcherYMDHHMMSS.matches()) {
            return 1;
        } else if (matcherYMDTHMS.matches()) {
            return 2;
        } else if (matcherYMDTHMSSSS.matches()) {
            return 3;
        } else {
            return 4;
        }

    }

    /**
     * @return LocalDateTime
     * @Title: date 当前日期
     * @Description: 当前日期开始时间
     */
    public static LocalDateTime dateStart(LocalDate date) {
        return LocalDateTime.of(date, LocalTime.MIN);
    }

    /**
     * @return LocalDateTime
     * @Title: date 当前日期
     * @Description: 当前日期结束时间
     */
    public static LocalDateTime dateEnd(LocalDate date) {
        String endDate = DateTimeUtil.formatDateTime(LocalDateTime.of(date, LocalTime.MAX));
        return DateTimeUtil.parseOfPattern(endDate);
    }

    /**
     * @return LocalDateTime
     * @Title: date 当前日期的月份起始时间
     * @Description: 当前日期的月份起始时间
     */
    public static LocalDateTime monthStart(LocalDateTime date) {
        return date.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
    }

    /**
     * @return LocalDateTime
     * @Title: date 当前日期的月份结束时间
     * @Description: 当前日期的月份结束时间
     */
    public static LocalDateTime monthEnd(LocalDateTime date) {
        LocalDateTime endMonthDay = date.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        return DateTimeUtil.parseOfPattern(DateTimeUtil.formatDateTime(endMonthDay));
    }


    /**
     * @return LocalDateTime
     * @Title: todayStart
     * @Description: 当天开始时间
     */
    public static LocalDateTime todayStart() {
        return LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
    }

    /**
     * @return LocalDateTime
     * @Title: tomorrowStart
     * @Description: 明天开始时间
     */
    public static LocalDateTime tomorrowStart() {
        return LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN);
    }

    public static LocalDateTime yearStart(LocalDateTime date) {
        return date.with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
    }

    /**
     * 获取下n个季度的第一天
     *
     * @return
     */
    public static LocalDateTime getQuarterMin(LocalDateTime dateTime, Integer quarter) {
        Integer diffMonths = dateTime.getMonthValue() % 3 == 0 ? 2 : (dateTime.getMonthValue() % 3 == 2 ? 1 : 0);
        dateTime = dateTime.plusMonths(quarter * 3 - diffMonths);
        LocalDateTime firstDay = dateTime.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
        return firstDay;
    }

    /**
     * 获取下n个季度的最后一天
     *
     * @return
     */
    public static LocalDateTime getQuarterMax(LocalDateTime dateTime, Integer quarter) {
        Integer diffMonths = dateTime.getMonthValue() % 3 == 0 ? 2 : (dateTime.getMonthValue() % 3 == 2 ? 1 : 0);
        dateTime = dateTime.plusMonths((quarter + 1) * 3 - diffMonths - 1);
        LocalDateTime lastDay = dateTime.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
        return lastDay;
    }

    /**
     * 获取指定的年月
     *
     * @param addMonth
     * @return
     */
    public static Integer getYearMonth(Integer addMonth) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMM");
        LocalDateTime time = LocalDateTime.now().plusMonths(addMonth);
        String localTime = df.format(time);
        return Integer.parseInt(localTime);
    }


    /***
     * 解析日期 为 LocalDateTime
     * @param dataStr 如: 2021-08-11
     * @return
     */
    public static LocalDateTime parseOfDate(String dataStr) {
        LocalDateTime dateTime = LocalDateTime.now();
        if (dataStr != null) {
            dateTime = LocalDateTime.parse(dataStr + "T01:00");
        }
        return dateTime;
    }

    /**
     * 获取两个时间点的月份差
     *
     * @param dt1 第一个时间点
     * @param dt2 第二个时间点
     * @return int，即需求的月数差
     */
    public static int monthDiff(LocalDateTime dt1, LocalDateTime dt2) {
        //获取第一个时间点的月份
        int month1 = dt1.getMonthValue();
        //获取第一个时间点的年份
        int year1 = dt1.getYear();
        //获取第一个时间点的月份
        int month2 = dt2.getMonthValue();
        //获取第一个时间点的年份
        int year2 = dt2.getYear();
        //返回两个时间点的月数差
        return (year2 - year1) * 12 + (month2 - month1);
    }

    public static Integer getCurrentYear() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        Date date = new Date();
        return Integer.parseInt(sdf.format(date));
    }

    public static Integer getCurrentMonth() {
        SimpleDateFormat sdf = new SimpleDateFormat("MM");
        Date date = new Date();
        return Integer.parseInt(sdf.format(date));
    }

    public static Integer getCurrentYear(LocalDateTime dateTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy");
        return Integer.valueOf(dateTime.format(formatter));
    }

    public static Integer getCurrentMonth(LocalDateTime dateTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM");
        return Integer.valueOf(dateTime.format(formatter));
    }


    public static LocalDateTime timestampToDatetime(long timestamp) {
        Instant instant = Instant.ofEpochSecond(timestamp);
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }


    public static boolean isWorkingDay(final LocalDateTime time) {
        return time.getDayOfWeek().getValue() < DayOfWeek.SATURDAY.getValue();
    }

    public static boolean isWorkingHours(final LocalDateTime time) {
        int hour = time.getHour();
        return WORK_HOUR_START <= hour && hour <= WORK_HOUR_END;
    }

    public static boolean isWorkingTime(final LocalDateTime time) {
        if (isWorkingDay(time) && isWorkingHours(time)) {
            return true;
        } else {
            return false;
        }
    }

    public static boolean isWorkingTime() {
        return isWorkingTime(LocalDateTime.now());
    }


}
